using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Cmc.Modbus.Base;
using NewLife;

namespace Cmc.Modbus
{
    public class DefaultReadWrite
    {
        private IClientFactory _clientFactory;
  
        /// <summary>
        /// 指令间隔时间（毫秒）
        /// </summary>
        public int SleepTime = 100;
        public ILog Log = new SimpleLog();
        private IBytesCmd GetByteCmd(ModbusCmdType modbusCmdType)
        {
            return modbusCmdType == ModbusCmdType.ModbusRtu ? rtu : tcp;
        }

        IBytesCmd rtu = new ModbusRtuCmd();
        IBytesCmd tcp = new ModbusTcpCmd();

        public DefaultReadWrite(IClientFactory clientFactory)
        {
            _clientFactory = clientFactory;
        }
        public virtual void WriteData(WriteData writeData)
        {
            var bytesCmd = GetByteCmd(writeData.ModbusCmdType);
            var client = _clientFactory.GetClient(writeData.CommunicationType, writeData.Ip, writeData.Port, writeData.Server);
            var address = writeData.AddressesInt;
            if (writeData.FunctionType == FunctionType.CoilStatus)
            {
                foreach (AddressWriteValue w in writeData.WriteDatas)
                {
                    var newCmd = bytesCmd.WriteCoilStatus(w.Address, w.WriteValueBool);
                    client.SendBytes(newCmd);
                    Thread.Sleep(SleepTime);
                }
            }
            else if (writeData.FunctionType == FunctionType.HoldingRegister)
            {
                foreach (int address1 in address)
                {
                    var adds = writeData.WriteDatas.Where(m => m.Address == address1).ToList();
                    if (adds.All(m => m.Position == null))
                    {
                        var last = adds.Last(m => m.Position == null);
                        var newCmd = bytesCmd.WriteHoldingRegister(last.Address, last.WriteValue);
                        client.SendBytes(newCmd);
                    }
                    else
                    {
                        var readData = ReadHRHighLowBytes(bytesCmd, client, address1);
                        if (readData == null)
                        {
                            throw new Exception("读取数据失败！");
                        }
                        else
                        {
                            var backData = readData.Addresses[address1];
                            var bits = OtherHelper.ToBitArray(backData.Low, backData.High);
                            foreach (AddressWriteValue addressWriteValue in adds)
                            {
                                if (addressWriteValue.Position != null && addressWriteValue.Position < bits.Length)
                                {
                                    bits[addressWriteValue.Position.Value] = addressWriteValue.WriteValueBool;
                                }
                            }
                            var newValue = OtherHelper.BitToIntOne(bits);
                            var newCmd = bytesCmd.WriteHoldingRegister(address1, newValue);
                            client.SendBytes(newCmd);
                        }
                    }
                    Thread.Sleep(SleepTime);
                }
            }
        }

        ReadDataBack ReadHRHighLowBytes(IBytesCmd bytesCmd, IClient client, int address)
        {
            //读回地址组装
            var cmdbytes = SendReadCmd(bytesCmd, FunctionType.HoldingRegister, address, 1, address);
            //client.SendBytes(cmdbytes); 
            var rdata = client.RecieveBytes(cmdbytes);
            if (rdata == null)
            {
                return null;
            }
            var readData = bytesCmd.ReciveDataHandle(client.IP, client.Port, rdata, address);
            if (readData.Addresses.ContainsKey(address))
            {
                return readData;
            }
            else
            {
                return null;
            }
        }

        public virtual List<ReadDataBack> ReadData(ReadData readData)
        {
            var bytesCmd = GetByteCmd(readData.ModbusCmdType);
            var client = _clientFactory.GetClient(readData.CommunicationType, readData.Ip, readData.Port, readData.Server);
            int lenth = readData.Length;
            int start = readData.StartAddress;
            List<ReadDataBack> readDataBacks = new List<ReadDataBack>();
            while (lenth > 0)
            {
                int currentLenth;
                if (lenth <= ConstConfig.OnceLenth)
                {
                    currentLenth = lenth;
                }
                else
                {
                    currentLenth = ConstConfig.OnceLenth;
                }

                try
                {
                    var cmdBytes = SendReadCmd(bytesCmd, readData.FunctionType, start, currentLenth, readData.Id, readData.StationNumber);
#if DEBUG
                    var bstring = cmdBytes.ToHex(" ");
                    if (readData.FunctionType == FunctionType.InputRegister)
                    {
                        Log.Info(bstring);
                    }
#endif
                    var recive = client.RecieveBytes(cmdBytes);
                    if (recive != null)
                    {
#if DEBUG
                        var rstring = recive.ToHex(" ");
                       Log.Info(rstring);
#endif
                        var readDataBack = bytesCmd.ReciveDataHandle(client.IP, client.Port, recive, start);
                        readDataBacks.Add(readDataBack);
                    }

                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }

                lenth = lenth - ConstConfig.OnceLenth;
                start = start + ConstConfig.OnceLenth;
                if (lenth > 0)
                {
                    Thread.Sleep(SleepTime); //间隔时间发送 
                }
            }

            return readDataBacks;
        }

        byte[] SendReadCmd(IBytesCmd bytesCmd, FunctionType functionType, int start, int length, int id = 0, byte flag = 0x01)
        {
            switch (functionType)
            {
                case FunctionType.InputStatus:
                    return bytesCmd.ReadInputStatus(start, length, id, flag);
                    break;
                case FunctionType.CoilStatus:
                    return bytesCmd.ReadCoilStatus(start, length, id, flag);
                    break;
                case FunctionType.HoldingRegister:
                    return bytesCmd.ReadHoldingRegister(start, length, id, flag);
                    break;
                case FunctionType.InputRegister:
                    return bytesCmd.ReadInputRegister(start, length, id, flag);
                    break;
            }  
            return new byte[0];
        }


    }
}
